packages<-c("adehabitatHR","data.table","ggfortify","grid","move","moveVis","OpenStreetMap","pbapply","plotly","rgdal","sp","tidyverse","viridis")
sapply(packages, require, character.only=T)
adehabitatHR data.table ggfortify grid move moveVis OpenStreetMap pbapply
TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
plotly rgdal sp tidyverse viridis
TRUE TRUE TRUE TRUE TRUE
data <- read.csv("bats.csv")
Plot 1
qaqc_plot <- ggplot() + geom_point(data=data,
aes(utm.easting,utm.northing,
color=individual.local.identifier)) +
labs(x="Easting", y="Northing") +
guides(color=guide_legend("Bat Identifier"))
ggplotly(qaqc_plot)
Plot 2
lapply(split(data, data$individual.local.identifier),
function(x)write.csv(x, file = paste(x$individual.local.identifier[1],".csv"), row.names = FALSE))
cannot open file 'Bat 1 .csv': Permission deniedError in file(file, ifelse(append, "a", "w")) :
cannot open the connection
files <- c("Bat 5 .csv", "Bat 6 .csv")
utm_points <- cbind(data$utm.easting, data$utm.northing)
utm_locations <- SpatialPoints(utm_points,
proj4string=CRS("+proj=utm +zone=14 +datum=WGS84"))
proj_lat.lon <- as.data.frame(spTransform(
utm_locations, CRS("+proj=longlat +datum=WGS84")))
colnames(proj_lat.lon) <- c("x","y")
raster <- openmap(c(max(proj_lat.lon$y)+0.01, min(proj_lat.lon$x)-0.01),
c(min(proj_lat.lon$y)-0.01, max(proj_lat.lon$x)+0.01),
type = "bing")
raster_utm <- openproj(raster,
projection = "+proj=utm +zone=14 +ellps=WGS84 +units=m +no_defs")
autoplot(raster_utm, expand = TRUE) + theme_bw() +
theme(legend.position="right") +
theme(panel.border = element_rect(colour = "black", fill=NA, size=1)) +
geom_point(data=data, aes(utm.easting,utm.northing,
color=individual.local.identifier), size = 3, alpha = 0.8) +
theme(axis.title = element_text(face="bold")) + labs(x="Easting",
y="Northing") + guides(color=guide_legend("Bat Identifier"))

Plot 3 - Minimum Convex Polygon
mcp_raster <- function(filename){
data <- read.csv(file = filename)
x <- as.data.frame(data$utm.easting)
y <- as.data.frame(data$utm.northing)
xy <- c(x,y)
data.proj <- SpatialPointsDataFrame(xy,data, proj4string = CRS("+proj=utm +zone=14 +ellps=WGS84 +units=m +no_defs"))
xy <- SpatialPoints(data.proj@coords)
mcp.out <- mcp(xy, percent=100, unout="ha")
mcp.points <- cbind((data.frame(xy)),data$individual.local.identifier)
colnames(mcp.points) <- c("x","y", "identifier")
mcp.poly <- fortify(mcp.out, region = "id")
units <- grid.text(paste(round(mcp.out@data$area,2),"ha"), x=0.85, y=0.95,
gp=gpar(fontface=4, col="white", cex=0.9), draw = FALSE)
mcp.plot <- autoplot(raster_utm, expand = TRUE) + theme_bw() + theme(legend.position="none") +
theme(panel.border = element_rect(colour = "black", fill=NA, size=1)) +
geom_polygon(data=mcp.poly, aes(x=mcp.poly$long, y=mcp.poly$lat), alpha=0.8) +
geom_point(data=mcp.points, aes(x=x, y=y)) +
labs(x="Easting (m)", y="Northing (m)", title=mcp.points$identifier) +
theme(legend.position="none", plot.title = element_text(face = "bold", hjust = 0.5)) +
annotation_custom(units)
mcp.plot
}
pblapply(files, mcp_raster)
| | 0 % ~calculating
|========================================= | 50% ~00s
|==================================================================================| 100% elapsed=00s
[[1]]
[[2]]


Plot 4 - Kernel-Density Estimation
kde_raster <- function(filename){
data <- read.csv(file = filename)
x <- as.data.frame(data$utm.easting)
y <- as.data.frame(data$utm.northing)
xy <- c(x,y)
data.proj <- SpatialPointsDataFrame(xy,data, proj4string = CRS("+proj=utm +zone=14 +ellps=WGS84 +units=m +no_defs"))
xy <- SpatialPoints(data.proj@coords)
kde<-kernelUD(xy, h="href", kern="bivnorm", grid=100)
ver <- getverticeshr(kde, 95)
kde.points <- cbind((data.frame(data.proj@coords)),data$individual.local.identifier)
colnames(kde.points) <- c("x","y","identifier")
kde.poly <- fortify(ver, region = "id")
units <- grid.text(paste(round(ver$area,2)," ha"), x=0.85, y=0.95,
gp=gpar(fontface=4, col="white", cex=0.9), draw = FALSE)
kde.plot <- autoplot(raster_utm, expand = TRUE) + theme_bw() + theme(legend.position="none") +
theme(panel.border = element_rect(colour = "black", fill=NA, size=1)) +
geom_polygon(data=kde.poly, aes(x=kde.poly$long, y=kde.poly$lat), alpha = 0.8) +
geom_point(data=kde.points, aes(x=x, y=y)) +
labs(x="Easting (m)", y="Northing (m)", title=kde.points$identifier) +
theme(legend.position="none", plot.title = element_text(face = "bold", hjust = 0.5)) +
annotation_custom(units)
kde.plot
}
pblapply(files, kde_raster)
| | 0 % ~calculating
|========================================= | 50% ~00s
|==================================================================================| 100% elapsed=00s
[[1]]
[[2]]


Plot 5 - Brownian Bridge Movement
date <- as.POSIXct(strptime(as.character(bat5$timestamp),"%Y-%m-%d %H:%M:%S"))
bat5$date <- date
bat5.reloc <- cbind.data.frame(bat5$utm.easting, bat5$utm.northing,
as.vector(bat5$individual.local.identifier),
as.POSIXct(date))
colnames(bat5.reloc) <- c("x","y","id","date")
trajectory <- as.ltraj(bat5.reloc, date=date, id="bat5")
sig1 <- liker(trajectory, sig2 = 58, rangesig1 = c(0, 5), plotit = FALSE)
bats.traj <- kernelbb(trajectory, sig1 = .7908, sig2 = 58, grid = 100)
bb_ver <- getverticeshr(bats.traj, 95)
bb_poly <- fortify(bb_ver, region = "id",
proj4string = CRS("+proj=utm +zone=14+
ellps=WGS84 +units=m +no_defs"))
colnames(bb_poly) <- c("x","y","order","hole","piece","id","group")
bb_image <- crop(bats.traj, bb_ver,
proj4string = CRS("+proj=utm +zone=14 +
ellps=WGS84 +units=m +no_defs"))
bb_units <- grid.text(paste(round(bb_ver$area,2)," ha"), x=0.85, y=0.95,
gp=gpar(fontface=4, col="white", cex=0.9), draw = FALSE)
bb.plot <- autoplot(raster_utm, expand = TRUE) + theme_bw() + theme(legend.position="none") +
theme(panel.border = element_rect(colour = "black", fill=NA, size=1)) +
geom_tile(data=bb_image,
aes(x=bb_image@coords[,1], y=bb_image@coords[,2],
fill = bb_image@data$ud)) +
geom_polygon(data=bb_poly, aes(x=x, y=y, group = group), color = "black", fill = NA) +
scale_fill_viridis_c(option = "inferno") + annotation_custom(bb_units) +
labs(x="Easting (m)", y="Northing (m)", title="bat5") +
theme(legend.position="none", plot.title = element_text(face = "bold", hjust = 0.5))
bb.plot

Animated Gif of Movement
bats.move <- move(x=bat5$location.long,
y=bat5$location.lat,
time=as.POSIXct(bat5$timestamp,
format="%Y-%m-%d %H:%M:%S"),
proj=CRS("+proj=longlat +ellps=WGS84 +datum=WGS84"),
data=bat5, animal=bat5$individual.local.identifier,
sensor=bat5$sensor.type)
movement <- align_move(bats.move, res = "max", digit = 0, unit = "secs")
frames <- frames_spatial(movement, path_colours = "red",
map_service = "osm",
alpha = 0.5) %>%
add_labels(x = "Longitude", y = "Latitude") %>%
add_northarrow() %>%
add_scalebar() %>%
add_timestamps(movement, type = "label") %>%
add_progress()
Processing movement data...
Approximated animation duration: ≈ 1.48s at 25 fps for 37 frames
| | 0 % ~calculating
|=== | 3 % ~02s
|===== | 5 % ~02s
|======= | 8 % ~02s
|========= | 11% ~02s
|============ | 14% ~02s
|============== | 16% ~02s
|================ | 19% ~02s
|================== | 22% ~02s
|==================== | 24% ~02s
|======================= | 27% ~02s
|========================= | 30% ~02s
|=========================== | 32% ~02s
|============================= | 35% ~02s
|================================ | 38% ~02s
|================================== | 41% ~02s
|==================================== | 43% ~02s
|====================================== | 46% ~02s
|======================================== | 49% ~01s
|=========================================== | 51% ~01s
|============================================= | 54% ~01s
|=============================================== | 57% ~01s
|================================================= | 59% ~01s
|=================================================== | 62% ~01s
|====================================================== | 65% ~01s
|======================================================== | 68% ~01s
|========================================================== | 70% ~01s
|============================================================ | 73% ~01s
|=============================================================== | 76% ~01s
|================================================================= | 78% ~01s
|=================================================================== | 81% ~01s
|===================================================================== | 84% ~00s
|======================================================================= | 86% ~00s
|========================================================================== | 89% ~00s
|============================================================================ | 92% ~00s
|============================================================================== | 95% ~00s
|================================================================================ | 97% ~00s
|==================================================================================| 100% elapsed=03s
Retrieving and compositing basemap imagery...
| | 0 % ~calculating
|====== | 7 % ~02s
|=========== | 13% ~01s
|================= | 20% ~01s
|====================== | 27% ~01s
|============================ | 33% ~01s
|================================= | 40% ~01s
|======================================= | 47% ~01s
|============================================ | 53% ~01s
|================================================== | 60% ~00s
|======================================================= | 67% ~00s
|============================================================= | 73% ~00s
|================================================================== | 80% ~00s
|======================================================================== | 87% ~00s
|============================================================================= | 93% ~00s
|==================================================================================| 100% elapsed=01s
no non-missing arguments to min; returning Infno non-missing arguments to max; returning -Infno non-missing arguments to min; returning Infno non-missing arguments to max; returning -Infno non-missing arguments to min; returning Infno non-missing arguments to max; returning -Inf
Assigning raster maps to frames...
| | 0 % ~calculating
|==================================================================================| 100% elapsed=00s
Creating frames...
animate_frames(frames, fps = 5, overwrite = TRUE,
out_file = "./moveVis-5fps.gif")
Rendering animation...
Approximated animation duration: ≈ 7.4s at 5 fps for 37 frames
| | 0 % ~calculating
|=== | 3 % ~02m 54s
|===== | 5 % ~02m 54s
|======= | 8 % ~02m 45s
|========= | 11% ~02m 42s
|============ | 14% ~02m 35s
|============== | 16% ~02m 29s
|================ | 19% ~02m 25s
|================== | 22% ~02m 20s
|==================== | 24% ~02m 16s
|======================= | 27% ~02m 11s
|========================= | 30% ~02m 06s
|=========================== | 32% ~02m 02s
|============================= | 35% ~01m 56s
|================================ | 38% ~01m 51s
|================================== | 41% ~01m 47s
|==================================== | 43% ~01m 41s
|====================================== | 46% ~01m 36s
|======================================== | 49% ~01m 32s
|=========================================== | 51% ~01m 27s
|============================================= | 54% ~01m 22s
|=============================================== | 57% ~01m 17s
|================================================= | 59% ~01m 13s
|=================================================== | 62% ~01m 08s
|====================================================== | 65% ~01m 03s
|======================================================== | 68% ~59s
|========================================================== | 70% ~54s
|============================================================ | 73% ~49s
|=============================================================== | 76% ~44s
|================================================================= | 78% ~39s
|=================================================================== | 81% ~34s
|===================================================================== | 84% ~30s
|======================================================================= | 86% ~25s
|========================================================================== | 89% ~20s
|============================================================================ | 92% ~15s
|============================================================================== | 95% ~10s
|================================================================================ | 97% ~05s
|==================================================================================| 100% elapsed=03m 03s
Frame 1 (2%)
Frame 2 (5%)
Frame 3 (8%)
Frame 4 (10%)
Frame 5 (13%)
Frame 6 (16%)
Frame 7 (18%)
Frame 8 (21%)
Frame 9 (24%)
Frame 10 (27%)
Frame 11 (29%)
Frame 12 (32%)
Frame 13 (35%)
Frame 14 (37%)
Frame 15 (40%)
Frame 16 (43%)
Frame 17 (45%)
Frame 18 (48%)
Frame 19 (51%)
Frame 20 (54%)
Frame 21 (56%)
Frame 22 (59%)
Frame 23 (62%)
Frame 24 (64%)
Frame 25 (67%)
Frame 26 (70%)
Frame 27 (72%)
Frame 28 (75%)
Frame 29 (78%)
Frame 30 (81%)
Frame 31 (83%)
Frame 32 (86%)
Frame 33 (89%)
Frame 34 (91%)
Frame 35 (94%)
Frame 36 (97%)
Frame 37 (100%)
Finalizing encoding... done!
LS0tDQp0aXRsZTogIkxhdXJlbidzIEhvbWUgUmFuZ2UgQW5hbHlzaXMiDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQpgYGB7ciBTZXR1cCwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCnBhY2thZ2VzPC1jKCJhZGVoYWJpdGF0SFIiLCJkYXRhLnRhYmxlIiwiZ2dmb3J0aWZ5IiwiZ3JpZCIsIm1vdmUiLCJtb3ZlVmlzIiwiT3BlblN0cmVldE1hcCIsInBiYXBwbHkiLCJwbG90bHkiLCJyZ2RhbCIsInNwIiwidGlkeXZlcnNlIiwidmlyaWRpcyIpDQpzYXBwbHkocGFja2FnZXMsIHJlcXVpcmUsIGNoYXJhY3Rlci5vbmx5PVQpDQpgYGANCg0KYGBge3IgSW1wb3J0IERhdGF9DQpkYXRhIDwtIHJlYWQuY3N2KCJiYXRzLmNzdiIpDQpgYGANCg0KDQojIFBsb3QgMSAgDQpgYGB7ciBJbml0aWFsIFBsb3R9DQpxYXFjX3Bsb3QgPC0gZ2dwbG90KCkgKyBnZW9tX3BvaW50KGRhdGE9ZGF0YSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFlcyh1dG0uZWFzdGluZyx1dG0ubm9ydGhpbmcsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvcj1pbmRpdmlkdWFsLmxvY2FsLmlkZW50aWZpZXIpKSArDQogICAgICAgICAgICAgICAgICAgICAgICBsYWJzKHg9IkVhc3RpbmciLCB5PSJOb3J0aGluZyIpICsNCiAgICAgICAgICAgICAgICAgICAgICAgIGd1aWRlcyhjb2xvcj1ndWlkZV9sZWdlbmQoIkJhdCBJZGVudGlmaWVyIikpDQoNCmdncGxvdGx5KHFhcWNfcGxvdCkNCmBgYA0KDQoNCiMgUGxvdCAyICAgDQpgYGB7ciBTcGxpdCBEYXRhc2V0LCBtZXNzYWdlPUZBTFNFfQ0KbGFwcGx5KHNwbGl0KGRhdGEsIGRhdGEkaW5kaXZpZHVhbC5sb2NhbC5pZGVudGlmaWVyKSwgDQogICAgICAgZnVuY3Rpb24oeCl3cml0ZS5jc3YoeCwgZmlsZSA9IHBhc3RlKHgkaW5kaXZpZHVhbC5sb2NhbC5pZGVudGlmaWVyWzFdLCIuY3N2IiksIHJvdy5uYW1lcyA9IEZBTFNFKSkNCmBgYA0KDQpgYGB7ciBGaWxlIExpc3R9DQpmaWxlcyA8LSBjKCJCYXQgNSAuY3N2IiwgIkJhdCA2IC5jc3YiKQ0KYGBgDQoNCmBgYHtyIFNwYXRpYWwgRGV0YWlsLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KdXRtX3BvaW50cyA8LSBjYmluZChkYXRhJHV0bS5lYXN0aW5nLCBkYXRhJHV0bS5ub3J0aGluZykNCnV0bV9sb2NhdGlvbnMgPC0gU3BhdGlhbFBvaW50cyh1dG1fcG9pbnRzLCANCiAgICAgICAgICAgICAgICAgcHJvajRzdHJpbmc9Q1JTKCIrcHJvaj11dG0gK3pvbmU9MTQgK2RhdHVtPVdHUzg0IikpDQpwcm9qX2xhdC5sb24gPC0gYXMuZGF0YS5mcmFtZShzcFRyYW5zZm9ybSgNCiAgICAgICAgICAgICAgICB1dG1fbG9jYXRpb25zLCBDUlMoIitwcm9qPWxvbmdsYXQgK2RhdHVtPVdHUzg0IikpKQ0KY29sbmFtZXMocHJval9sYXQubG9uKSA8LSBjKCJ4IiwieSIpDQpyYXN0ZXIgPC0gb3Blbm1hcChjKG1heChwcm9qX2xhdC5sb24keSkrMC4wMSwgbWluKHByb2pfbGF0LmxvbiR4KS0wLjAxKSwgDQogICAgICAgICAgICAgICAgICBjKG1pbihwcm9qX2xhdC5sb24keSktMC4wMSwgbWF4KHByb2pfbGF0LmxvbiR4KSswLjAxKSwgDQogICAgICAgICAgICAgICAgICB0eXBlID0gImJpbmciKQ0KcmFzdGVyX3V0bSA8LSBvcGVucHJvaihyYXN0ZXIsIA0KICAgICAgICAgICAgICBwcm9qZWN0aW9uID0gIitwcm9qPXV0bSArem9uZT0xNCArZWxscHM9V0dTODQgK3VuaXRzPW0gK25vX2RlZnMiKQ0KYGBgDQoNCmBgYHtyIERpc3BsYXkgUmFzdGVyIHdpdGggVVRNIE92ZXJsYXl9DQphdXRvcGxvdChyYXN0ZXJfdXRtLCBleHBhbmQgPSBUUlVFKSArIHRoZW1lX2J3KCkgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb249InJpZ2h0IikgKw0KICB0aGVtZShwYW5lbC5ib3JkZXIgPSBlbGVtZW50X3JlY3QoY29sb3VyID0gImJsYWNrIiwgZmlsbD1OQSwgc2l6ZT0xKSkgKw0KICBnZW9tX3BvaW50KGRhdGE9ZGF0YSwgYWVzKHV0bS5lYXN0aW5nLHV0bS5ub3J0aGluZywNCiAgICAgICAgICAgICBjb2xvcj1pbmRpdmlkdWFsLmxvY2FsLmlkZW50aWZpZXIpLCBzaXplID0gMywgYWxwaGEgPSAwLjgpICsNCiAgdGhlbWUoYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChmYWNlPSJib2xkIikpICsgbGFicyh4PSJFYXN0aW5nIiwNCiAgICAgICAgeT0iTm9ydGhpbmciKSArIGd1aWRlcyhjb2xvcj1ndWlkZV9sZWdlbmQoIkJhdCBJZGVudGlmaWVyIikpDQpgYGANCg0KDQojIFBsb3QgMyAtIE1pbmltdW0gQ29udmV4IFBvbHlnb24gIA0KYGBge3IgTWluaW11bSBDb252ZXggUG9seWdvbn0NCm1jcF9yYXN0ZXIgPC0gZnVuY3Rpb24oZmlsZW5hbWUpew0KICBkYXRhIDwtIHJlYWQuY3N2KGZpbGUgPSBmaWxlbmFtZSkNCiAgeCA8LSBhcy5kYXRhLmZyYW1lKGRhdGEkdXRtLmVhc3RpbmcpDQogIHkgPC0gYXMuZGF0YS5mcmFtZShkYXRhJHV0bS5ub3J0aGluZykNCiAgeHkgPC0gYyh4LHkpDQogIGRhdGEucHJvaiA8LSBTcGF0aWFsUG9pbnRzRGF0YUZyYW1lKHh5LGRhdGEsIHByb2o0c3RyaW5nID0gQ1JTKCIrcHJvaj11dG0gK3pvbmU9MTQgK2VsbHBzPVdHUzg0ICt1bml0cz1tICtub19kZWZzIikpDQogIHh5IDwtIFNwYXRpYWxQb2ludHMoZGF0YS5wcm9qQGNvb3JkcykNCiAgbWNwLm91dCA8LSBtY3AoeHksIHBlcmNlbnQ9MTAwLCB1bm91dD0iaGEiKQ0KICBtY3AucG9pbnRzIDwtIGNiaW5kKChkYXRhLmZyYW1lKHh5KSksZGF0YSRpbmRpdmlkdWFsLmxvY2FsLmlkZW50aWZpZXIpDQogIGNvbG5hbWVzKG1jcC5wb2ludHMpIDwtIGMoIngiLCJ5IiwgImlkZW50aWZpZXIiKQ0KICBtY3AucG9seSA8LSBmb3J0aWZ5KG1jcC5vdXQsIHJlZ2lvbiA9ICJpZCIpDQogIHVuaXRzIDwtIGdyaWQudGV4dChwYXN0ZShyb3VuZChtY3Aub3V0QGRhdGEkYXJlYSwyKSwiaGEiKSwgeD0wLjg1LCAgeT0wLjk1LA0KICAgICAgICAgICAgICAgICAgICAgZ3A9Z3Bhcihmb250ZmFjZT00LCBjb2w9IndoaXRlIiwgY2V4PTAuOSksIGRyYXcgPSBGQUxTRSkNCiAgbWNwLnBsb3QgPC0gYXV0b3Bsb3QocmFzdGVyX3V0bSwgZXhwYW5kID0gVFJVRSkgKyB0aGVtZV9idygpICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIikgKw0KICAgIHRoZW1lKHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfcmVjdChjb2xvdXIgPSAiYmxhY2siLCBmaWxsPU5BLCBzaXplPTEpKSArDQogICAgZ2VvbV9wb2x5Z29uKGRhdGE9bWNwLnBvbHksIGFlcyh4PW1jcC5wb2x5JGxvbmcsIHk9bWNwLnBvbHkkbGF0KSwgYWxwaGE9MC44KSArDQogICAgZ2VvbV9wb2ludChkYXRhPW1jcC5wb2ludHMsIGFlcyh4PXgsIHk9eSkpICsgDQogICAgbGFicyh4PSJFYXN0aW5nIChtKSIsIHk9Ik5vcnRoaW5nIChtKSIsIHRpdGxlPW1jcC5wb2ludHMkaWRlbnRpZmllcikgKw0KICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIsIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoZmFjZSA9ICJib2xkIiwgaGp1c3QgPSAwLjUpKSArIA0KICAgIGFubm90YXRpb25fY3VzdG9tKHVuaXRzKQ0KICBtY3AucGxvdA0KfQ0KDQpwYmxhcHBseShmaWxlcywgbWNwX3Jhc3RlcikNCmBgYA0KDQoNCiMgUGxvdCA0IC0gS2VybmVsLURlbnNpdHkgRXN0aW1hdGlvbiAgDQpgYGB7ciBLZXJuZWwtRGVuc2l0eSBFc3RpbWF0aW9uLCB3YXJuaW5nPUZBTFNFfQ0Ka2RlX3Jhc3RlciA8LSBmdW5jdGlvbihmaWxlbmFtZSl7DQogIGRhdGEgPC0gcmVhZC5jc3YoZmlsZSA9IGZpbGVuYW1lKQ0KICB4IDwtIGFzLmRhdGEuZnJhbWUoZGF0YSR1dG0uZWFzdGluZykNCiAgeSA8LSBhcy5kYXRhLmZyYW1lKGRhdGEkdXRtLm5vcnRoaW5nKQ0KICB4eSA8LSBjKHgseSkNCiAgZGF0YS5wcm9qIDwtIFNwYXRpYWxQb2ludHNEYXRhRnJhbWUoeHksZGF0YSwgcHJvajRzdHJpbmcgPSBDUlMoIitwcm9qPXV0bSArem9uZT0xNCArZWxscHM9V0dTODQgK3VuaXRzPW0gK25vX2RlZnMiKSkNCiAgeHkgPC0gU3BhdGlhbFBvaW50cyhkYXRhLnByb2pAY29vcmRzKQ0KICBrZGU8LWtlcm5lbFVEKHh5LCBoPSJocmVmIiwga2Vybj0iYml2bm9ybSIsIGdyaWQ9MTAwKQ0KICB2ZXIgPC0gZ2V0dmVydGljZXNocihrZGUsIDk1KQ0KICBrZGUucG9pbnRzIDwtIGNiaW5kKChkYXRhLmZyYW1lKGRhdGEucHJvakBjb29yZHMpKSxkYXRhJGluZGl2aWR1YWwubG9jYWwuaWRlbnRpZmllcikNCiAgY29sbmFtZXMoa2RlLnBvaW50cykgPC0gYygieCIsInkiLCJpZGVudGlmaWVyIikNCiAga2RlLnBvbHkgPC0gZm9ydGlmeSh2ZXIsIHJlZ2lvbiA9ICJpZCIpDQogIHVuaXRzIDwtIGdyaWQudGV4dChwYXN0ZShyb3VuZCh2ZXIkYXJlYSwyKSwiIGhhIiksIHg9MC44NSwgIHk9MC45NSwNCiAgICAgICAgICAgICAgICAgICAgIGdwPWdwYXIoZm9udGZhY2U9NCwgY29sPSJ3aGl0ZSIsIGNleD0wLjkpLCBkcmF3ID0gRkFMU0UpDQogIGtkZS5wbG90IDwtIGF1dG9wbG90KHJhc3Rlcl91dG0sIGV4cGFuZCA9IFRSVUUpICsgdGhlbWVfYncoKSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpICsNCiAgICB0aGVtZShwYW5lbC5ib3JkZXIgPSBlbGVtZW50X3JlY3QoY29sb3VyID0gImJsYWNrIiwgZmlsbD1OQSwgc2l6ZT0xKSkgKw0KICAgIGdlb21fcG9seWdvbihkYXRhPWtkZS5wb2x5LCBhZXMoeD1rZGUucG9seSRsb25nLCB5PWtkZS5wb2x5JGxhdCksIGFscGhhID0gMC44KSArDQogICAgZ2VvbV9wb2ludChkYXRhPWtkZS5wb2ludHMsIGFlcyh4PXgsIHk9eSkpICsNCiAgICBsYWJzKHg9IkVhc3RpbmcgKG0pIiwgeT0iTm9ydGhpbmcgKG0pIiwgdGl0bGU9a2RlLnBvaW50cyRpZGVudGlmaWVyKSArDQogICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIiwgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChmYWNlID0gImJvbGQiLCBoanVzdCA9IDAuNSkpICsgDQogICAgYW5ub3RhdGlvbl9jdXN0b20odW5pdHMpDQogIGtkZS5wbG90DQp9DQoNCnBibGFwcGx5KGZpbGVzLCBrZGVfcmFzdGVyKQ0KYGBgDQoNCg0KIyBQbG90IDUgLSBCcm93bmlhbiBCcmlkZ2UgTW92ZW1lbnQNCmBgYHtyIEJyb3duaWFuIEJyaWRnZSBNb3ZlbWVudH0NCmJhdDUgPC0gcmVhZC5jc3YoIkJhdCA1IC5jc3YiKQ0KZGF0ZSA8LSBhcy5QT1NJWGN0KHN0cnB0aW1lKGFzLmNoYXJhY3RlcihiYXQ1JHRpbWVzdGFtcCksIiVZLSVtLSVkICVIOiVNOiVTIikpDQpiYXQ1JGRhdGUgPC0gZGF0ZQ0KYmF0NS5yZWxvYyA8LSBjYmluZC5kYXRhLmZyYW1lKGJhdDUkdXRtLmVhc3RpbmcsIGJhdDUkdXRtLm5vcnRoaW5nLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhcy52ZWN0b3IoYmF0NSRpbmRpdmlkdWFsLmxvY2FsLmlkZW50aWZpZXIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhcy5QT1NJWGN0KGRhdGUpKQ0KY29sbmFtZXMoYmF0NS5yZWxvYykgPC0gYygieCIsInkiLCJpZCIsImRhdGUiKQ0KdHJhamVjdG9yeSA8LSBhcy5sdHJhaihiYXQ1LnJlbG9jLCBkYXRlPWRhdGUsIGlkPSJiYXQ1IikNCnNpZzEgPC0gbGlrZXIodHJhamVjdG9yeSwgc2lnMiA9IDU4LCByYW5nZXNpZzEgPSBjKDAsIDUpLCBwbG90aXQgPSBGQUxTRSkNCmJhdHMudHJhaiA8LSBrZXJuZWxiYih0cmFqZWN0b3J5LCBzaWcxID0gLjc5MDgsIHNpZzIgPSA1OCwgZ3JpZCA9IDEwMCkNCmJiX3ZlciA8LSBnZXR2ZXJ0aWNlc2hyKGJhdHMudHJhaiwgOTUpDQpiYl9wb2x5IDwtIGZvcnRpZnkoYmJfdmVyLCByZWdpb24gPSAiaWQiLCANCiAgICAgICAgICAgICAgICAgICBwcm9qNHN0cmluZyA9IENSUygiK3Byb2o9dXRtICt6b25lPTE0Kw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVsbHBzPVdHUzg0ICt1bml0cz1tICtub19kZWZzIikpDQpjb2xuYW1lcyhiYl9wb2x5KSA8LSBjKCJ4IiwieSIsIm9yZGVyIiwiaG9sZSIsInBpZWNlIiwiaWQiLCJncm91cCIpDQpiYl9pbWFnZSA8LSBjcm9wKGJhdHMudHJhaiwgYmJfdmVyLCANCiAgICAgICAgICAgICAgICAgcHJvajRzdHJpbmcgPSBDUlMoIitwcm9qPXV0bSArem9uZT0xNCArDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVsbHBzPVdHUzg0ICt1bml0cz1tICtub19kZWZzIikpDQpiYl91bml0cyA8LSBncmlkLnRleHQocGFzdGUocm91bmQoYmJfdmVyJGFyZWEsMiksIiBoYSIpLCB4PTAuODUsICB5PTAuOTUsDQogICAgICAgICAgICAgICAgICAgZ3A9Z3Bhcihmb250ZmFjZT00LCBjb2w9IndoaXRlIiwgY2V4PTAuOSksIGRyYXcgPSBGQUxTRSkNCmJiLnBsb3QgPC0gYXV0b3Bsb3QocmFzdGVyX3V0bSwgZXhwYW5kID0gVFJVRSkgKyB0aGVtZV9idygpICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIikgKw0KICB0aGVtZShwYW5lbC5ib3JkZXIgPSBlbGVtZW50X3JlY3QoY29sb3VyID0gImJsYWNrIiwgZmlsbD1OQSwgc2l6ZT0xKSkgKw0KICBnZW9tX3RpbGUoZGF0YT1iYl9pbWFnZSwgDQogICAgICAgICAgICBhZXMoeD1iYl9pbWFnZUBjb29yZHNbLDFdLCB5PWJiX2ltYWdlQGNvb3Jkc1ssMl0sDQogICAgICAgICAgICBmaWxsID0gYmJfaW1hZ2VAZGF0YSR1ZCkpICsNCiAgZ2VvbV9wb2x5Z29uKGRhdGE9YmJfcG9seSwgYWVzKHg9eCwgeT15LCBncm91cCA9IGdyb3VwKSwgY29sb3IgPSAiYmxhY2siLCBmaWxsID0gTkEpICsNCiAgc2NhbGVfZmlsbF92aXJpZGlzX2Mob3B0aW9uID0gImluZmVybm8iKSArIGFubm90YXRpb25fY3VzdG9tKGJiX3VuaXRzKSArDQogIGxhYnMoeD0iRWFzdGluZyAobSkiLCB5PSJOb3J0aGluZyAobSkiLCB0aXRsZT0iYmF0NSIpICsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIiwgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChmYWNlID0gImJvbGQiLCBoanVzdCA9IDAuNSkpDQpiYi5wbG90DQpgYGANCg0KDQojIEFuaW1hdGVkIEdpZiBvZiBNb3ZlbWVudA0KYGBge3IgR2lmIDF9DQpiYXRzLm1vdmUgPC0gbW92ZSh4PWJhdDUkbG9jYXRpb24ubG9uZywgDQogICAgICAgICAgICAgeT1iYXQ1JGxvY2F0aW9uLmxhdCwgDQogICAgICAgICAgICAgdGltZT1hcy5QT1NJWGN0KGJhdDUkdGltZXN0YW1wLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZm9ybWF0PSIlWS0lbS0lZCAlSDolTTolUyIpLCANCiAgICAgICAgICAgICBwcm9qPUNSUygiK3Byb2o9bG9uZ2xhdCArZWxscHM9V0dTODQgK2RhdHVtPVdHUzg0IiksDQogICAgICAgICAgICAgZGF0YT1iYXQ1LCBhbmltYWw9YmF0NSRpbmRpdmlkdWFsLmxvY2FsLmlkZW50aWZpZXIsIA0KICAgICAgICAgICAgIHNlbnNvcj1iYXQ1JHNlbnNvci50eXBlKQ0KYGBgDQoNCmBgYHtyIEdpZiAyfQ0KbW92ZW1lbnQgPC0gYWxpZ25fbW92ZShiYXRzLm1vdmUsIHJlcyA9ICJtYXgiLCBkaWdpdCA9IDAsIHVuaXQgPSAic2VjcyIpDQoNCmBgYA0KDQpgYGB7ciBHaWYgMywgd2FybmluZz1GQUxTRX0NCmZyYW1lcyA8LSBmcmFtZXNfc3BhdGlhbChtb3ZlbWVudCwgcGF0aF9jb2xvdXJzID0gInJlZCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgbWFwX3NlcnZpY2UgPSAib3NtIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgYWxwaGEgPSAwLjUpICU+JSANCiAgYWRkX2xhYmVscyh4ID0gIkxvbmdpdHVkZSIsIHkgPSAiTGF0aXR1ZGUiKSAlPiUNCiAgYWRkX25vcnRoYXJyb3coKSAlPiUgDQogIGFkZF9zY2FsZWJhcigpICU+JSANCiAgYWRkX3RpbWVzdGFtcHMobW92ZW1lbnQsIHR5cGUgPSAibGFiZWwiKSAlPiUgDQogIGFkZF9wcm9ncmVzcygpDQoNCmBgYA0KDQpgYGB7ciBHaWYgNCAtIEFuaW1hdGV9DQphbmltYXRlX2ZyYW1lcyhmcmFtZXMsIGZwcyA9IDUsIG92ZXJ3cml0ZSA9IFRSVUUsDQogICAgICAgICAgICAgICBvdXRfZmlsZSA9ICIuL21vdmVWaXMtNWZwcy5naWYiKQ0KYGBgDQoNCg==